package com.hmall.extension;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ExtensionLoader<T> {

    private static final Map<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>();
    private final Map<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
    private static final Map<String, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>();

    public static <S> ExtensionLoader<S> getExtensionLoader(Class<S> type) {
        if (type == null) {
            throw new IllegalArgumentException("Extension type should not be null.");
        }
//        if (type.isInterface()) {
//            throw new IllegalArgumentException("Extension type should not be an interface.");
//        }
        ExtensionLoader<S> extensionLoader = (ExtensionLoader<S>) EXTENSION_LOADERS.get(type);
        if (extensionLoader == null) {
            EXTENSION_LOADERS.putIfAbsent(type, extensionLoader = new ExtensionLoader<S>());
            extensionLoader = (ExtensionLoader<S>) EXTENSION_LOADERS.get(type);
        }
        return extensionLoader;
    }

    public T getExtension(String Clazz) {
        if (Clazz == null || Clazz.trim().isEmpty()) {
            throw new IllegalArgumentException("Clazz should not be null.");
        }
        Holder<Object> holder = cachedInstances.get(Clazz);
        if (holder == null) {
            cachedInstances.put(Clazz,new Holder<>());
            holder = cachedInstances.get(Clazz);
        }
        Object instance = holder.get();
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    instance = createExtension(Clazz);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }

    private T createExtension(String clazz) {
        try {
            Class<?> classes = ExtensionLoader.class.getClassLoader().loadClass(clazz);
            if (clazz == null || clazz.trim().isEmpty()) {
                throw new RuntimeException("No such extension of clazz " + clazz);
            }
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
                EXTENSION_INSTANCES.put(clazz,classes.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            return instance;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

}
